﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CartItemEngine.cs" company="DST Nexdox">
//   Copyright (c) DST Nexdox. All rights reserved.
// </copyright>
// <summary>
//   Defines the CartItemEngine type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace BusinessEngines
{
  using System;
  using System.Collections.Generic;
  using System.Linq;

  using BusinessEngineInterfaces;
  using Entities;
  using Exceptions;
  using UnityRepository.Interfaces;

  public class CartItemEngine : ICartItemEngine
  {
    private readonly ICartItemRepository cartItemRepository;
    private readonly IDocumentRepository documentRepository;
    private readonly IManCoRepository manCoRepository;
    private readonly ICheckOutRepository checkOutRepository;
    private readonly ICheckOutEngine checkOutEngine;

    public CartItemEngine(ICartItemRepository cartItemRepository, IDocumentRepository documentRepository, IManCoRepository manCoRepository, ICheckOutRepository checkOutRepository, ICheckOutEngine checkOutEngine)
    {
      this.cartItemRepository = cartItemRepository;
      this.documentRepository = documentRepository;
      this.manCoRepository = manCoRepository;
      this.checkOutRepository = checkOutRepository;
      this.checkOutEngine = checkOutEngine;
    }

    public AddToCartResults AddGrid(String userName, String cartId, String grid)
    {
      ManCo manCoForCart = this.manCoRepository.GetManCoForCart(cartId);

      IEnumerable<Document> documents = this.documentRepository.GetDocumentsIncludingCheckOutEntity(grid);

      return this.AddDocuments(userName, cartId, manCoForCart, documents);
    }

    public AddToCartResults AddItems(String userName, String cartId, IEnumerable<String> documentIds)
    {
      ManCo manCoForCart = this.manCoRepository.GetManCoForCart(cartId);

      IEnumerable<Document> documents = this.documentRepository.GetDocuments(documentIds);

      return this.AddDocuments(userName, cartId, manCoForCart, documents);
    }

    private AddToCartResults AddDocuments(String userName, String cartId, ManCo basketManCo, IEnumerable<Document> documents)
    {
      var processor = new DocumentProcessor(basketManCo);
      foreach (Document document in documents)
      {
        processor.ProcessDocument(document);
      }

      List<Document> documentsToAdd = processor.GetDocumentsToAdd();
      AddToCartResults results = processor.GetResults();

      IEnumerable<CheckOut> checkOuts =
        documentsToAdd.Select(d => new CheckOut { CheckOutBy = userName, CheckOutDate = DateTime.Now, DocumentId = d.Id });

      IEnumerable<CartItem> cartItems = documentsToAdd.Select(d => new CartItem { CartId = cartId, DocumentId = d.Id });

      this.checkOutRepository.Create(checkOuts);
      this.cartItemRepository.Create(cartItems);

      return results;
    }

    private class DocumentProcessor
    {
      private readonly AddToCartResults addToCartResults = new AddToCartResults();
      private readonly List<Document> documentsToAdd = new List<Document>();

      private readonly ManCo basketManCo;
      private Int32? firstDocumentManCo;

      public DocumentProcessor(ManCo basketManCo)
      {
        this.basketManCo = basketManCo;
      }

      public void ProcessDocument(Document document)
      {
        if (this.CheckManCoIsValid(document))
        {
          return;
        }

        if (!this.CheckDocumentIsNotCheckedOut(document))
        {
          return;
        }

        this.AddDocumentToCollections(document);
      }

      public AddToCartResults GetResults()
      {
        return this.addToCartResults;
      }

      public List<Document> GetDocumentsToAdd()
      {
        return this.documentsToAdd;
      }

      private Boolean CheckManCoIsValid(Document document)
      {
        if (this.firstDocumentManCo == null)
        {
          this.firstDocumentManCo = document.ManCoId;
        }
        else
        {
          if (document.ManCoId != this.firstDocumentManCo)
          {
            // As the UI only allows you to add documents to a basket as part of a GridRun, this should
            // never be the case.
            throw new UnityException("Multiple ManCos in document list");
          }
        }

        if (this.basketManCo != null)
        {
          if (document.ManCo.Id != this.basketManCo.Id)
          {
            this.addToCartResults.DocumentsWithDifferentManCoToBasket.Add(document.DocumentId);
            return true;
          }
        }

        return false;
      }

      private Boolean CheckDocumentIsNotCheckedOut(Document document)
      {
        if (document.CheckOut != null)
        {
          this.addToCartResults.DocumentsAlreadyCheckedOut.Add(document.DocumentId);
          return false;
        }

        return true;
      }

      private void AddDocumentToCollections(Document document)
      {
        this.documentsToAdd.Add(document);
        this.addToCartResults.DocumentsAdded.Add(document.DocumentId);
      }
    }
  }
}
